#include "lib/pvmsystem.h"
#include "lib/decoders/wav/pvmsourcefactorywav.h"
#include "lib/decoders/wav/pvmvoicesourcewav.h"

#include "lib/memory/pvmmemory.h"

PVMSourceFatcoryWAV::PVMSourceFatcoryWAV()
{
}

PVMSourceFatcoryWAV::~PVMSourceFatcoryWAV()
{
}

pvm_bool PVMSourceFatcoryWAV::CanLoad(IPvmInStream* inStream, pvm_int8** startData, pvm_int* startDataSize)
{
	int readRes;
	char waveHdr[16];//RIFF+size+WAVE+fmt
	int sizeToCopy = *startDataSize;

	if(sizeToCopy>16)
		sizeToCopy = 16;

	if(startData)
		memcpy(waveHdr, *startData, sizeToCopy);

	if(*startDataSize<16)
	{
		readRes = inStream->Read(&waveHdr[sizeToCopy], 16-sizeToCopy);
		int oldSizeToCopy = sizeToCopy;
		if(readRes!=0)
		{
			sizeToCopy += readRes;
			PVMMemoryFree(*startData);
			*startData = (pvm_int8*)PVMMemoryAlloc(sizeToCopy);
			if(*startData)
			{
				*startDataSize = sizeToCopy;
				memcpy(*startData, waveHdr, sizeToCopy);
			}
			else
			{
				startDataSize = 0;
			}
		}

		if(readRes != 16 - oldSizeToCopy)
		{
			return false;
		}
	}

	if(*((int*)&waveHdr[0])!=0x46464952)//'RIFF'
		return false;

	if(*((int*)&waveHdr[8])!=0x45564157)//'WAVE'
		return false;

	if(*((int*)&waveHdr[12])!=0x20746d66)//'fmt '
		return false;

	return true;
}

IPVMVoiceSource* PVMSourceFatcoryWAV::CreateVoiceSource(IPvmInStream* inStream, pvm_int8* /*startData*/, pvm_int /*startDataSize*/)
{
	int readRes;
	const int waveHdrStartSize = 20;
	char waveHdrStart[waveHdrStartSize];//"RIFF"+size+"WAVE:+"fmt "+size

	inStream->Seek(0, EPVM_SEEK_BEGIN);
	readRes = inStream->Read(&waveHdrStart, waveHdrStartSize);
	if(readRes!=waveHdrStartSize)
	{
		return NULL;
	}

	if(*((int*)&waveHdrStart[0])!=0x46464952)//'RIFF'
	{
		return NULL;
	}

	if(*((int*)&waveHdrStart[8])!=0x45564157)//'WAVE'
	{
		return NULL;
	}

	if(*((int*)&waveHdrStart[12])!=0x20746d66)//'fmt '
	{
		return NULL;
	}

	FmtHeader fmtHeader;
	memcpy(&fmtHeader, &waveHdrStart[12], 20);

	readRes = inStream->Read(&fmtHeader.audioFormat, sizeof(FmtHeader) - 8);
	if(readRes!=sizeof(FmtHeader) - 8)
		return NULL;

	if(fmtHeader.audioFormat!=1 && fmtHeader.audioFormat!=0x11)//PCM and IMA ADPCM
	{
		//non PCM format
		return NULL;
	}

	int dataChunkOffset = 20 + fmtHeader.subchunk1Size;
	int dataChunkName = 0;
	int dataSize = 0;

	for(;;)
	{
		if(inStream->Seek(dataChunkOffset, EPVM_SEEK_BEGIN)==false)
			return NULL;

		if(inStream->Read(&dataChunkName, 4)!=4)
			return NULL;
		if(inStream->Read(&dataSize, 4)!=4)
			return NULL;

		const char* sig = "data";
		if(dataChunkName!=*((int*)sig))
		{
			dataChunkOffset += dataSize + 8;
		}
		else
		{
			break;
		}

		if(dataChunkOffset>inStream->Size())
			return NULL;


	}


	int dataOffset = dataChunkOffset + 8;

	pvm_int8* dataInitial = NULL;
	int dataInitialSize = 0;

	IPVMVoiceSource* retSource = PVMVoiceSourceWAV::Create(inStream, dataInitial, dataInitialSize, dataOffset, dataSize, fmtHeader);

	if(retSource==NULL && dataInitial!=NULL)
	{
		delete[](dataInitial);
	}

	return retSource;
}

IPVMSourceFatcory* PVMSourceFatcoryWAV::CreateFactory()
{
	return new PVMSourceFatcoryWAV();
}
